# -*- coding: utf-8 -*-
# @Date    : 2016-08-16 20:16:19
# @Author  : fancy (fancy@thecover.cn)

import gc
import weakref
from pprint import pprint

class Graph(object):
    def __init__(self, name):
        self.name = name
        self.other = None

    def set_next(self, other):
        print '%s.set_next(%r' % (self.name, other)
        self.other = other

    def all_nodes(self):
        yield self
        n = self.other
        while n and n.name != self.name:
            yield n
            n = n.other
        if n is self:
            yield n
        return

    def __str__(self):
        return '->'.join(n.name for n in self.all_nodes())

    def __repr__(self):
        return '<%s at 0x%x name=%s>' % (
            self.__class__.__name__, id(self), self.name)

    def __del__(self):
        print '(Delete %s)' % self.name


class WeakGraph(Graph):
    def set_next(self, other):
        if other is not None:
            if self in other.all_nodes():
                other = weakref.proxy(other)
        super(WeakGraph, self).set_next(other)
        return


def collect_and_show_garbage():
    print 'Collecting...'
    n = gc.collect()
    print 'unreachable objects', n
    print 'garbege'
    pprint(gc.garbage)

def demo(graph_factory):
    print 'set up graph'
    one = graph_factory('one')
    two = graph_factory('two')
    three = graph_factory('three')
    one.set_next(two)
    two.set_next(three)
    three.set_next(one)

    print
    print 'Graph'
    print str(one)
    collect_and_show_garbage()

    print
    three = None
    two = None
    print 'After 2 references removed'
    print str(one)
    collect_and_show_garbage()

    print
    print 'removeing last reference'
    one = None
    collect_and_show_garbage()

gc.set_debug(gc.DEBUG_LEAK)
print 'Setting up the cycle'
print
demo(WeakGraph)
print
print 'breaking the cycle and cleaning up garbage'
print
gc.garbage[0].set_next(None)
while gc.garbage:
    del gc.garbage[0]

print collect_and_show_garbage()
